<?php
class H2o_Context extends ArrayObject {
    private $arrayMethods = array('first'=> 0, 'last'=> 1, 'length'=> 2, 'size'=> 3);
    
    function __construct($scope = array()) {
        $this->scopes = array($scope);
    }

    function resolve($path) {
        $parts = explode('.', $path);
        $object = $this[array_shift($parts)];
        foreach($parts as $part) {
            if (is_array($object)) {
                if (isset($object[$part]) || is_a($object, 'ArrayObject'))
                    $object =& $object[$part];
                elseif (isset($this->arrayMethods[$part])) {
                    $size = count($object);
                    if ($part === 'first')
                        $object =& $object[0];
                    elseif ($part === 'last')
                        $object =& $object[$size -1];
                    else return $size;
                }
                else return null;              
            } 
            elseif (is_object($object)) {
                if (isset($object->part))
                    $object =& $object->$part;
                elseif (method_exists($object, $part))
                    $object = $object->$part();
                else return null;
            } else return null;
        }
        return $object;
    }
    
    function pop() {
        array_shift($this->scopes);
    }
    
    function push($stack = array()) {
        array_unshift($this->scopes, $stack);
    }
 
    function offsetGet($key) {
        if (strpos($key, '.') !== false)
            return $this->resolve($key);
        
        foreach ($this->scopes as &$layer) {
            if (isset($layer[$key]))
                return $layer[$key];
        }
    }

    function offsetExists($key) {
        return $this->offsetGet($key) !== null;
    }

    function offsetSet($key, $value) {
        $this->scopes[0][$key] = $value;
    }
}
 
class H2o_Block {
    var $name, $depth, $super;
    var $context;
    
    function __construct($name, $depth, $context) {
        $this->depth = $depth;
        $this->name = $name;
        $this->context = $context;
    }

    function __toString() {
        $callback = "block_{$this->name}_{$this->depth}";
        $this->context->push(array('block'=>$this));
        $callback($this->context);
        $this->context->pop();
        return '';
    }
 
    static function register($name, $depth, $c) {
        $blocks = array();
        for ($i=0; $i < $depth; $i++) {
            $blocks[$i] = $block =& new H2o_Block($name, $i+1, $c);
            if ($i) {
                $block->super =& $blocks[$i-1];
            }
        }
        return end($blocks);
    }
}


class H2o_Template {
    function __construct($file) {
        $this->file = $file;
    }
    
    function render($context) {
        echo $this->generate($context);    
    }
    
    function generate($context) {
        ob_start();
        $c = new H2o_Context($context);
        include $this->file.".compile.php";
        return ob_get_clean();
    }
}
?>